
#include <stddef.h>
#include <string.h>
#include <Utility.h>
#include "jlib_list.h"

#include "FreeRTOS.h"
#include "Timers.h"
#include "semphr.h"

#include "BLE_if.h"
#include "BLE.h"

#include <Reader/Reader.h>

#if 1
#undef LOCAL_LOG
#define LOCAL_LOG	1

#undef LOCAL_TRACE
#define LOCAL_TRACE		1
#endif 

#if 0
#ifndef  NDEBUG
#define NDEBUG
#endif 
#endif 

#include "Log.h"

#define NO_PROC()		((void)0)
#define NO_VAL		

#define CHECK_RETURN(cond, val, proc)	do{if(cond){proc; return val;}}while(0)

#define ERR_RETURN(cond, msg)			if(cond){TRACE_E(msg); return ;}
#define ERR_RETURN_VAL(cond, msg, val)	if(cond){TRACE_E(msg); return (val);}

#define STX		0x5A
#define ETX		0xA5

#pragma pack(1)

typedef struct {
	uint8_t stx;
	uint8_t length;
	uint8_t code;
	uint8_t data[];
}BLEFrame_T;

#pragma pack()

static void BLEFrame_Print(const BLEFrame_T* frame){
	LLOGD(NULL, "len=%d ", frame->length);
	LLOGD(NULL, "code=%04X ", frame->code);
	LLOG_BUFS("data", frame->data, frame->length);
	LLOGD(NULL, "xor=%02X ", frame->data[frame->length]);
}

static uint8_t Buffer_BCC(const uint8_t* buffer, uint32_t length){
	uint32_t i;
	uint8_t bcc = 0;
	
	for(i = 0; i < length; i++){
		bcc ^= buffer[i];
	}

	return bcc;
}

static inline uint32_t BLEFrame_Size(const BLEFrame_T* frame){
	return frame->length + 4; //加上 STX LEN BCC ETX 等字节
}

static inline uint8_t BLEFrame_BCC(const BLEFrame_T* frame){
	return frame->data[frame->length-1];
}

static uint8_t BLEFrame_CalcBCC(const BLEFrame_T* frame){
	uint32_t length = frame->length + 1;	//加上Len本身
	const uint8_t* buffer = (const uint8_t*)frame + 1;
	uint32_t i;
	uint8_t bcc = 0;
	
	for(i = 0; i < length; i++){
		bcc ^= buffer[i];
	}

	return bcc;
}

static int BLEFrame_Verify(const BLEFrame_T* frame){
	uint8_t bcc1 = BLEFrame_CalcBCC(frame);
	uint8_t bcc2 = BLEFrame_BCC(frame);
	return bcc1 == bcc2;
}

int BLE_IF_Receive(uint8_t* buffer, uint32_t length, uint32_t wtime);
void BLE_IF_Send(const uint8_t *buffer, uint32_t length);

static inline void BLE_IF_SendByte(uint8_t byte){
	BLE_IF_Send(&byte, 1);
}

static inline int BLE_IF_ReceiveByte(uint8_t* byte, int wtime){
	int len = BLE_IF_Receive(byte, 1, wtime);
	if(len != 1){
		return -1;
	}
	return 0;
}

static inline int BLE_IF_ReceiveBuf(void* buffer, uint32_t length, int wtime){
	uint8_t* buf = buffer;
	int len = 0;
	uint32_t t = wtime;
	uint32_t t0 = xTaskGetTickCount();
	
	while(len < length){
		int ret = BLE_IF_Receive(buf + len, length - len, t);
		if(ret <= 0){
			break;
		}
		uint32_t cunsume = xTaskGetTickCount() - t0;
		if(cunsume > t){
			t = 0;
		}
		else{
			t -= cunsume;
		}
		len += ret;
	}
	
	return len;
}

void BLE_Send_Frame(uint8_t code, const uint8_t* buf, uint16_t len){
	uint8_t length = len;
	const uint8_t* buffer = buf;
	uint8_t head[3] = {STX, (length + 1), code};
	uint8_t bcc = Buffer_BCC(head+1, sizeof(head)-1);
	BLE_IF_Clear();
	LLOGI(NULL, "BLE_Send_Frame");
	LLOG_BUFS("head", head, sizeof(head));
	BLE_IF_Send(head, sizeof(head));
	if(length > 0){
		bcc ^= Buffer_BCC(buffer, length);
		LLOG_BUFS("data", buffer, length);
		BLE_IF_Send(buffer, length);
	}
	uint8_t tail[2] = {bcc, ETX};
	LLOG_BUFS("tail", tail, sizeof(tail));
	BLE_IF_Send(tail, sizeof(tail));
}

int BLE_Receive_Frame(uint8_t *code, uint8_t* buf, uint16_t* rlen, int wtime){
	uint8_t byte;
	uint8_t length;
	uint8_t bcc1 = 0;
	LLOGI(NULL, "BLE_Receive_Frame");
	ERR_RETURN_VAL(BLE_IF_ReceiveByte(&byte, wtime), "STX fail!", -1);
	if(byte != STX){
		LTRACE_E("Error STX(%X)!", byte);
		return -2;
	}
	ERR_RETURN_VAL(BLE_IF_ReceiveByte(&length, 10), "LEN fail!", -3);
	bcc1 ^= length; 
	ERR_RETURN_VAL(BLE_IF_ReceiveByte(code, 10), "CODE fail!", -4);
	bcc1 ^= *code;
	uint8_t head[3] = {STX, length, *code};
	LLOG_BUFS("head", head, sizeof(head));
	
	int i;
	uint8_t rx;
	uint8_t len = length - 1;
	for(i = 0; i < len; i++){
		ERR_RETURN_VAL(BLE_IF_ReceiveByte(&rx, 10), "Data fail!", -5);
		if(buf){
			buf[i] = rx;
		}
		bcc1 ^= rx;
	}
	if(len > 0 && buf){
		LLOG_BUFS("data", buf, len);
	}
	if(rlen){
		*rlen = len;
	}

	uint8_t bcc;
	ERR_RETURN_VAL(BLE_IF_ReceiveByte(&bcc, 10), "BCC fail!", -6);
	ERR_RETURN_VAL(BLE_IF_ReceiveByte(&byte, 10), "ETX fail!", -7);
	uint8_t tail[2] = {bcc, byte};
	LLOG_BUFS("tail", tail, sizeof(tail));
	if(byte != ETX){
		LTRACE_E("Error ETX(%X)!", byte);
		return -8;
	}

	if(bcc != bcc1){
		LTRACE_E("bcc(%02X) != bcc1(%02X)", bcc, bcc1);
		return -9;
	}
	
	return 0;
}

static void BLEFrame_Send(BLEPack_T* cmd){
	uint8_t length = cmd->data->len;
	const uint8_t* buffer = cmd->data->data;
	uint8_t bcc = Buffer_BCC(buffer, length);
	BLE_IF_Clear();
	BLE_IF_SendByte(STX);
	BLE_IF_SendByte(length + 1);
	bcc ^= (length + 1);
	BLE_IF_SendByte(cmd->code);
	bcc ^= cmd->code;
	BLE_IF_Send(buffer, length);
	BLE_IF_SendByte(bcc);
	BLE_IF_SendByte(ETX);
}

static int BLEFrame_Receive(BLEPack_T* resp, int wtime){
	uint8_t byte;
	uint8_t length;

	ERR_RETURN_VAL(BLE_IF_ReceiveByte(&byte, wtime), "STX fail!", -1);
	if(byte != STX){
		LTRACE_E("Error STX(%X)!", byte);
		return -2;
	}

	ERR_RETURN_VAL(BLE_IF_ReceiveByte(&length, 10), "LEN fail!", -3);
	ERR_RETURN_VAL(BLE_IF_ReceiveByte(&resp->code, 10), "CODE fail!", -4);
	
	int i;
	uint8_t* data = resp->data->data;
	uint8_t len = length - 1;
	for(i = 0; i < len; i++){
		ERR_RETURN_VAL(BLE_IF_ReceiveByte(data + i, 10), "Data fail!", -5);
	}
	resp->data->len = len;

	uint8_t bcc;
	ERR_RETURN_VAL(BLE_IF_ReceiveByte(&bcc, 10), "BCC fail!", -6);
	ERR_RETURN_VAL(BLE_IF_ReceiveByte(&byte, 10), "ETX fail!", -7);
	if(byte != ETX){
		LTRACE_E("Error ETX(%X)!", byte);
		return -8;
	}

	uint8_t bcc1 = Buffer_BCC(data, len);
	bcc1 ^= length; bcc1 ^= resp->code;
	if(bcc != bcc1){
		LTRACE_E("bcc(%02X) != bcc1(%02X)", bcc, bcc1);
		return -9;
	}
	
	return 0;
}

int BLE_Command(BLEPack_T* cmd, BLEPack_T* resp, int wtime){
	BLEFrame_Send(cmd);
	return BLEFrame_Receive(resp, wtime);
}

int BLE_Command2(BLEPack_T* cmd, BLEPack_T* resp, int wtime){
	BLE_Send_Frame(cmd->code, cmd->data->data, cmd->data->len);
	return BLE_Receive_Frame(&resp->code, resp->data->data, &resp->data->len, wtime);
}

int BLE_ActionWait(uint8_t cmd, const uint8_t* param, uint16_t len, int wtime){
	int ret;
	uint8_t code = 1;
	
	BLE_Send_Frame(cmd, param, len);
	ret = BLE_Receive_Frame(&code, NULL, NULL, wtime);
	CHECK_RETURN(ret, ret, NO_PROC());
	CHECK_RETURN(code, -2, TRACE_E("code=%02X", code));
	
	return ret;
}

int BLE_Action(uint8_t cmd, const uint8_t* param, uint16_t len){
	return BLE_ActionWait(cmd, param, len, 200);
}

int BLE_Request(uint8_t cmd, uint8_t* resp, uint16_t *rlen, int wtime){
	int ret;
	uint8_t code = 1;
	
	BLE_Send_Frame(cmd, NULL, NULL);
	ret = BLE_Receive_Frame(&code, resp, rlen, wtime);
	CHECK_RETURN(ret, ret, NO_PROC());
	CHECK_RETURN(code, -2, NO_PROC());
	
	return ret;
}

int BLE_Init(uint8_t state){
	int ret = BLE_Action(BLE_Cmd_Init, &state, sizeof(state));
	if(!ret){
		reader.ble.state = state;
	}
	
	return ret;
}

int BLE_GetState(uint8_t* state){
	return BLE_Request(BLE_Cmd_GetState, state, NULL, 100);
}

int BLE_SetName(const uint8_t name[], uint8_t length){
	if(length > 26){
		TRACE_W("length(%d) > 10", length);
	}
	
	return BLE_Action(BLE_Cmd_SetName, name, length);
}

int BLE_GetName(uint8_t name[], uint8_t *length){
	uint16_t rlen;
	int ret = BLE_Request(BLE_Cmd_GetName, name, &rlen, 200);
	CHECK_RETURN(ret, ret, NO_PROC());
	*length = rlen;
	return ret;
}

int BLE_Reset(void){
	return BLE_Action(BLE_Cmd_Reset, NULL, 0);
}

int BLE_SetMCUInfo(const MCU_Info_T* info){
	return BLE_Action(BLE_Cmd_SetMCUInfo, (const uint8_t *) info, sizeof(*info));
}

int BLE_GetMCUInfo(MCU_Info_T* info){
	return BLE_Request(BLE_Cmd_GetMCUInfo, (uint8_t *) info, NULL, 100);
}


int BLE_DFUBoot(void){
	BLE_IF_TriggerRTS();
	return BLE_Action(BLE_Cmd_DFUBoot, NULL, 0);
}

int BLE_DFUpgrade(uint8_t* buf, uint16_t length){
	return BLE_ActionWait(BLE_Cmd_DFUpgrade, buf, length, 500);
}

int BLE_DFUFinish(uint8_t buf[], uint8_t len){
	return BLE_ActionWait(BLE_Cmd_DFUFinish, buf, len, 500);
}

/*-----------------------------------------------------------------------*/
#define BOOT_STX		0xAA
#define BOOT_ETX		0xBB

int BLE_UpgradeService(void){
	static const uint8_t bleUpgradCmd[] = {0xAA, 0x00, 0x00, 0x2A, 0x06, 0xBB};
	static const uint8_t bleUpgradResp[] = {0xAA, 0x00, 0x00, 0x2B, 0x07, 0xBB};
	uint8_t buffer[8];
	
	if(BLE_IF_ReceiveByte(buffer, 0)){
		return -1;
	}

	if(buffer[0] != BOOT_STX){
		LTRACE_E("Error STX(%X)!", buffer[0]);
		return -2;
	}
	
//		ERR_RETURN_VAL(BLE_IF_ReceiveBuf(buffer+1, sizeof(bleUpgradCmd)-1, 5000), "Rx fail!", -3);
	int rlen = BLE_IF_ReceiveBuf(buffer+1, sizeof(bleUpgradCmd)-1, 50);
	if(rlen != (sizeof(bleUpgradCmd)-1)){
		LTRACE_E("Rx Fail! len = %d", rlen);
		LTRACE_BUFS(buffer, rlen);
		return -3;
	}

	if(memcmp(bleUpgradCmd, buffer, sizeof(bleUpgradCmd))){
		LTRACE_E("Cmd");
		LTRACE_BUFS(buffer, 5);
		return -4;
	}
	
	BLE_IF_Send(bleUpgradResp, sizeof(bleUpgradResp));
	
	return 0;
}

